package com.sys.utils;

import org.snmp4j.*;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.PDUFactory;
import org.snmp4j.util.TableEvent;
import org.snmp4j.util.TableUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2020/4/10/010.
 */
public class SnmpUtils {

    /**
     * 服务器进程集合信息
     */
    public static List<String> collectService(String ip, String password) {
        String[] oids = {"1.3.6.1.4.1.77.1.2.3.1.1"};
        List<TableEvent> list = collect(oids, ip, password);

        List<String> resultList = new ArrayList<>();
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null) continue;
            String name = values[0].getVariable().toString();//name
            resultList.add(getChinese(name));
        }

        return resultList;
    }

    /**
     * 服务器进程集合信息
     */
    public static List<Map<String, Object>> collectProcess(String ip, String password) {
        String[] oids =
                {"1.3.6.1.2.1.25.4.2.1.1",  //index
                        "1.3.6.1.2.1.25.4.2.1.2",  //name
                        "1.3.6.1.2.1.25.4.2.1.4",  //run path
                        "1.3.6.1.2.1.25.4.2.1.6",  //type
                        "1.3.6.1.2.1.25.5.1.1.1",  //cpu
                        "1.3.6.1.2.1.25.5.1.1.2"}; //memory
        List<TableEvent> list = collect(oids, ip, password);

        List<Map<String, Object>> resultMapList = new ArrayList<>();
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null) continue;
            String index = values[0].getVariable().toString();//index
            String name = values[1].getVariable().toString();//name
            String type = values[0].getVariable().toString();//type
            String cpu = values[4].getVariable().toString();//cpu
            String memory = values[5].getVariable().toString();//memory
            String path = values[2].getVariable().toString();//path

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("index", Integer.valueOf(index));
            resultMap.put("name", name);
            resultMap.put("type", type);
            resultMap.put("cpu", Long.valueOf(cpu));
            resultMap.put("memory", Long.valueOf(memory));
            resultMap.put("path", path);
            resultMapList.add(resultMap);
        }

        return resultMapList;
    }

    /**
     * 服务器安装软件集合
     */
    public static List<Map<String, Object>> collectSoft(String ip, String password) {
        String[] oids = {"1.3.6.1.2.1.25.6.3.1.2",  //software
                "1.3.6.1.2.1.25.6.3.1.4",  //type
                "1.3.6.1.2.1.25.6.3.1.5"}; //install date
        List<TableEvent> list = collect(oids, ip, password);

        List<Map<String, Object>> resultMapList = new ArrayList<>();
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null) continue;
            String software = values[0].getVariable().toString();//software
            String type = values[1].getVariable().toString();//type
            String date = values[2].getVariable().toString();//date

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("software", getChinese(software));
            resultMap.put("type", type);
            resultMap.put("date", hexToDateTime(date.replace("'", "")));
            resultMapList.add(resultMap);
        }
        return resultMapList;
    }

    /**
     * 获取磁盘相关信息
     *
     * @return
     */
    public static List<Map<String, Object>> collectDisk(String ip, String password) {
        String DISK_OID = "1.3.6.1.2.1.25.2.1.4";
        String[] oids = {"1.3.6.1.2.1.25.2.3.1.2",  //type 存储单元类型
                "1.3.6.1.2.1.25.2.3.1.3",  //descr
                "1.3.6.1.2.1.25.2.3.1.4",  //unit 存储单元大小
                "1.3.6.1.2.1.25.2.3.1.5",  //size 总存储单元数
                "1.3.6.1.2.1.25.2.3.1.6"}; //used 使用存储单元数;
        List<TableEvent> list = collect(oids, ip, password);

        List<Map<String, Object>> resultMapList = new ArrayList<>();

        Map<String, Object> allMap = new HashMap<>();
        allMap.put("drive", "all"); // 盘符
        allMap.put("size", 0.0); // 大小
        allMap.put("usedSize", 0.0); // 使用大小
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null || !DISK_OID.equals(values[0].getVariable().toString())) continue;
            Double unit = Double.valueOf(values[2].getVariable().toString());//unit 存储单元大小
            Double totalSize = Double.valueOf(values[3].getVariable().toString());//size 总存储单元数
            Double usedSize = Double.valueOf(values[4].getVariable().toString());//used  使用存储单元数

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("drive", getChinese(values[1].getVariable().toString())); // 盘符
            resultMap.put("size", totalSize * unit / (1024 * 1024 * 1024)); // 大小
            resultMap.put("usedSize", usedSize * unit / (1024 * 1024 * 1024)); // 使用大小

            allMap.put("size", (Double) allMap.get("size") + totalSize * unit / (1024 * 1024 * 1024)); // 大小
            allMap.put("usedSize", (Double) allMap.get("usedSize") + usedSize * unit / (1024 * 1024 * 1024)); // 使用大小
            resultMapList.add(resultMap);
        }

        resultMapList.add(allMap);

        return resultMapList;
    }

    /**
     * 内存
     *
     * @param ip
     * @param password
     * @return
     */
    public static Map<String, Double> collectMemory(String ip, String password) {
        String[] oids = {"1.3.6.1.2.1.25.2.3.1.2",  //type 内存单元类型
                "1.3.6.1.2.1.25.2.3.1.3",  //descr
                "1.3.6.1.2.1.25.2.3.1.4",  //unit 内存单元大小
                "1.3.6.1.2.1.25.2.3.1.5",  //size 总内存单元数
                "1.3.6.1.2.1.25.2.3.1.6"}; //used 使用内存单元数;
        String PHYSICAL_MEMORY_OID = "1.3.6.1.2.1.25.2.1.2";//物理内存
        String VIRTUAL_MEMORY_OID = "1.3.6.1.2.1.25.2.1.3"; //虚拟内存

        List<TableEvent> list = collect(oids, ip, password);

        Map<String, Double> resultMap = new HashMap<>();
        resultMap.put("unit", 0.0);//unit 存储单元大小
        resultMap.put("totalSize", 0.0);//size 总存储单元数
        resultMap.put("usedSize", 0.0);//used  使用存储单元数
        resultMap.put("physicalMemory", 0.0); // 物理内存大小G
        resultMap.put("physicalUseMemory", 0.0); // 内存使用率
        resultMap.put("virtualMemory", 0.0); // 物理内存大小G
        resultMap.put("virtualUseMemory", 0.0); // 内存使用率
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null) continue;

            Double unit = Double.valueOf(values[2].getVariable().toString());//unit 存储单元大小
            Double totalSize = Double.valueOf(values[3].getVariable().toString());//size 总存储单元数
            Double usedSize = Double.valueOf(values[4].getVariable().toString());//used  使用存储单元数
            resultMap.put("unit", resultMap.get("unit") + unit);
            resultMap.put("totalSize", resultMap.get("totalSize") + totalSize);
            resultMap.put("usedSize", resultMap.get("usedSize") + usedSize);

            String oid = values[0].getVariable().toString();
            if (PHYSICAL_MEMORY_OID.equals(oid)) {
                resultMap.put("physicalMemory", resultMap.get("physicalMemory") + totalSize * unit / 1024 / 1024); // 物理内存大小M
                resultMap.put("physicalUseMemory", usedSize * 100 / totalSize);// 内存使用率
            } else if (VIRTUAL_MEMORY_OID.equals(oid)) {
                resultMap.put("virtualMemory", resultMap.get("virtualMemory") + totalSize * unit / 1024 / 1024); // 虚拟内存大小M
                resultMap.put("virtualUseMemory", usedSize * 100 / totalSize);// 虚拟使用率
            }
        }
        return resultMap;
    }

    /**
     * CPU的当前负载，N个核就有N个负载
     *
     * @param ip
     * @param password
     * @return
     */
    public static Map<String, Double> collectCPU(String ip, String password) {
        String[] oids = {"1.3.6.1.2.1.25.3.3.1.2"};

        List<TableEvent> list = collect(oids, ip, password);
        Map<String, Double> resultMap = new HashMap<>();
        Double load = 0.0;
        for (TableEvent event : list) {
            VariableBinding[] values = event.getColumns();
            if (values == null) continue;
            load += Integer.parseInt(values[0].getVariable().toString());
        }
        resultMap.put("load", load); // 负载
        resultMap.put("count", Double.valueOf(list.size())); // cpu核数
        resultMap.put("usePercentage", load / Double.valueOf(list.size())); // 使用率

        return resultMap;
    }

    /**
     * 连接snmp
     *
     * @param oids
     * @param ip
     * @param password
     * @return
     */
    public static List<TableEvent> collect(String[] oids, String ip, String password) {
        TransportMapping transport = null;
        Snmp snmp = null;
        CommunityTarget target;

        try {
            transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);//创建snmp
            snmp.listen();//监听消息
            target = new CommunityTarget();
            target.setCommunity(new OctetString(password));
            target.setRetries(2); // 通信不成功时的重试次数
            target.setAddress(GenericAddress.parse("udp:" + ip + "/161"));
            target.setTimeout(8000); // 超时时间
            target.setVersion(SnmpConstants.version2c);
            TableUtils tableUtils = new TableUtils(snmp, new PDUFactory() {
                @Override
                public PDU createPDU(MessageProcessingModel messageProcessingModel) {
                    PDU request = new PDU();
                    request.setType(PDU.GET);
                    return request;
                }

                @Override
                public PDU createPDU(Target arg0) {
                    PDU request = new PDU();
                    request.setType(PDU.GET);
                    return request;
                }
            });
            OID[] columns = new OID[oids.length];
            for (int i = 0; i < oids.length; i++) {
                columns[i] = new OID(oids[i]);
            }
            List<TableEvent> list = tableUtils.getTable(target, columns, null, null);
            if (list.size() == 1 && list.get(0).getColumns() == null) {
                return null;
            } else {
                return list;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (transport != null)
                    transport.close();
                if (snmp != null)
                    snmp.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取磁盘的中文名字
     * 解决snmp4j中文乱码问题
     */

    public static String getChinese(String octetString) {
        if (octetString == null || "".equals(octetString)
                || "null".equalsIgnoreCase(octetString)) return "";
        try {
            String[] temps = octetString.split(":");
            if (temps.length < 4)
                return octetString;
            byte[] bs = new byte[temps.length];
            for (int i = 0; i < temps.length; i++)
                bs[i] = (byte) Integer.parseInt(temps[i], 16);
            return new String(bs, "GB2312");
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 将16进制的时间转换成标准的时间格式
     */
    private static String hexToDateTime(String hexString) {
        if (hexString == null || "".equals(hexString))
            return "";
        String dateTime = "";
        try {
            byte[] values = OctetString.fromHexString(hexString).getValue();
            int year, month, day, hour, minute;

            year = values[0] * 256 + 256 + values[1];
            month = values[2];
            day = values[3];
            hour = values[4];
            minute = values[5];

            char format_str[] = new char[22];
            int index = 3;
            int temp = year;
            for (; index >= 0; index--) {
                format_str[index] = (char) (48 + (temp - temp / 10 * 10));
                temp /= 10;
            }
            format_str[4] = '-';
            index = 6;
            temp = month;
            for (; index >= 5; index--) {
                format_str[index] = (char) (48 + (temp - temp / 10 * 10));
                temp /= 10;
            }
            format_str[7] = '-';
            index = 9;
            temp = day;
            for (; index >= 8; index--) {
                format_str[index] = (char) (48 + (temp - temp / 10 * 10));
                temp /= 10;
            }
            format_str[10] = ' ';
            index = 12;
            temp = hour;
            for (; index >= 11; index--) {
                format_str[index] = (char) (48 + (temp - temp / 10 * 10));
                temp /= 10;
            }
            format_str[13] = ':';
            index = 15;
            temp = minute;
            for (; index >= 14; index--) {
                format_str[index] = (char) (48 + (temp - temp / 10 * 10));
                temp /= 10;
            }
            dateTime = new String(format_str, 0, format_str.length).substring(0, 16);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dateTime;
    }

}
